package cc.rengu.igas.opms.core.service.trans.apppay;

import cc.rengu.chargecalc.mchntfee.CalcMchntFee;
import cc.rengu.chargecalc.mchntfee.enums.CalcFlagEnum;
import cc.rengu.chargecalc.mchntfee.model.CalcMchntFeeResultBean;
import cc.rengu.chargecalc.mchntfee.model.CalcMchntFeeBean;
import cc.rengu.igas.acct.facade.bean.MchtAdjustDetail;
import cc.rengu.igas.acct.facade.request.VirtAcctMultiMchntAdjustRequest;
import cc.rengu.igas.momp.facade.base.Header;
import cc.rengu.igas.momp.facade.bean.PaymentOrderMchntFeeDiscountInfoBean;
import cc.rengu.igas.momp.facade.request.ComboMchntFeeDiscountRequest;
import cc.rengu.igas.momp.facade.request.ComboMchntFeeDiscountReversalRequest;
import cc.rengu.igas.momp.facade.response.ComboMchntFeeDiscountResponse;
import cc.rengu.igas.momp.facade.response.ComboMchntFeeDiscountReversalResponse;
import cc.rengu.igas.opms.common.constant.OpmsServiceConstant;
import cc.rengu.igas.opms.common.constant.OpmsTreeNodeConstant;
import cc.rengu.igas.opms.common.constant.OpmsParamConstant;
import cc.rengu.igas.opms.common.entity.TransLimitBean;
import cc.rengu.igas.opms.common.enums.CardTypeEnum;
import cc.rengu.igas.opms.common.enums.FeeTypeEnum;
import cc.rengu.igas.opms.common.enums.RespCodeEnum;
import cc.rengu.igas.opms.common.enums.SignTypeEnum;
import cc.rengu.igas.opms.common.util.IntegralUtil;
import cc.rengu.igas.opms.core.service.base.PayService;
import cc.rengu.igas.opms.facade.request.ScorePayRequest;
import cc.rengu.igas.opms.facade.response.ScorePayResponse;
import cc.rengu.igas.share.common.constant.ShareParamConstant;
import cc.rengu.igas.share.core.model.MchntInfo;
import cc.rengu.igas.share.core.model.PlatMchntInfo;
import cc.rengu.igas.share.core.model.UserInfo;
import cc.rengu.igas.share.core.realize.MchntService;
import cc.rengu.igas.share.core.realize.UserService;
import cc.rengu.igas.share.core.realize.impl.MchntServiceImpl;
import cc.rengu.igas.share.core.realize.impl.UserServiceImpl;
import cc.rengu.jradp.asynnotify.AsynNotify;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.PayOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.PayOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.oltp.service.common.enums.*;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.model.OrderInfo;
import cc.rengu.oltp.service.model.TransLimitInfo;
import cc.rengu.oltp.service.realize.*;
import cc.rengu.oltp.service.realize.impl.*;
import cc.rengu.oltp.utility.util.*;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 手机银行纯积分支付
 *
 * @author keyi
 * @modify by wangcheng
 * @date 2020/3/24.
 */

/**
 * @author Jinan Liu
 * @modified date: 2020/4/11 17:31
 * @description 按照营销修改后接口更改代码, 请检查TODO部分代码, 不确定应该如何修改
 */
public class ScorePayService extends PayService implements TransCustomImpl {

    private boolean isCouponTrans = false;
    private String acctType;
    private String prodBizType;

    @Override
    protected Object bizBeforeCust() throws Exception {
        ScorePayRequest scorePayRequest = new ScorePayRequest();
        ConvertUtil.convertOutput(scorePayRequest);
        return scorePayRequest;
    }

    @Override
    protected BizResponse bizProcessCust(Object request) throws Exception {
        //获取请求值
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
        List<ChannelCallCfg> esbChannelCallCfgList = txnInfo.getChannelCallCfgList().stream().filter(item -> item.getCallChannelId().equals(ChannelEnum.ESB.getChannelType())).sorted(Comparator.comparing(ChannelCallCfg::getCallIndex)).collect(Collectors.toList());
        List<ChannelCallCfg> mompChannelCallCfgList = txnInfo.getChannelCallCfgList().stream().filter(item -> item.getCallChannelId().equals(ChannelEnum.MOMP.getChannelType())).sorted(Comparator.comparing(ChannelCallCfg::getCallIndex)).collect(Collectors.toList());
        ScorePayRequest scorePayRequest = (ScorePayRequest) request;
        String instId = scorePayRequest.getHeader().getInstId();
        String txnNum = scorePayRequest.getHeader().getTxnNum();
        String bizType = scorePayRequest.getHeader().getBizType();
        String transAmt = scorePayRequest.getTransScoreCash();
        String transCode = scorePayRequest.getTransScoreNum();

        MchntService mchntService = new MchntServiceImpl();
        SysParamService sysParamService = new SysParamServiceImpl();

        //纯积分支付不支持用户使用卡劵
        isCouponTrans = scorePayRequest.getCouponInfo() != null && !StringUtil.isEmptyOrNull(scorePayRequest.getCouponInfo().getDiscountId());
        if (isCouponTrans) {
            rglog.info("纯积分支付方式不支持使用卡劵");
            throw new BizException(RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespCode(), RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespDesc());
        }

        //step1.订单检查
        OrderService orderService = new OrderServiceImpl();
        OrderInfo orderInfo = orderService.selectOrderInfoByUserOrderId(instId, scorePayRequest.getUserOrderId());
        if (null == orderInfo) {
            rglog.error("订单信息不存在!");
            throw new BizException(RespCodeEnum.ORDER_NOEXIT.getRespCode(), RespCodeEnum.ORDER_NOEXIT.getRespDesc());
        }
        UserOrderInfo userOrderInfo = orderInfo.getUserOrderInfo();
        List<MchntOrderInfo> mchntOrderInfos = orderInfo.getMchntOrderInfoList();

        //step2. 密码校验-加密平台
        if (!StringUtil.isEmptyOrNull(scorePayRequest.getPassword())) {
            checkPassword(scorePayRequest);
        }

        //获取账户类型以及批量手续费计算时产品代码
        getAcctType(sysParamService, instId, scorePayRequest.getAcctNo());

        List<TransLimitBean> transLimitBeanList = new ArrayList<>();
        for (MchntOrderInfo mchntOrderInfo : mchntOrderInfos) {

            if ("00".equals(mchntOrderInfo.getBizSceneType())) {
                rglog.info("购买卡劵的交易不允许使用纯积分支付,bizSceneType:<{}>", mchntOrderInfo.getBizSceneType());
                throw new BizException(RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespCode(), RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespDesc());
            }

            //3.商户权限检查
            boolean checkResult = mchntService.checkMchntAuth(instId, mchntOrderInfo.getMchntNo(), null, txnNum, bizType, acctType);
            if (!checkResult) {
                rglog.error("商户<{}>无此交易权限!", mchntOrderInfo.getMchntNo());
                throw new BizException(RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespCode(), RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespDesc());
            }
            //step4.商户限额检查
            MchntInfo mchntInfo = mchntService.getMchntInfo(instId, mchntOrderInfo.getMchntNo());
            if (null == mchntInfo) {
                rglog.error("查询商户基本信息失败<{}>", mchntOrderInfo.getMchntNo());
                throw new BizException(RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespCode(), RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespDesc());
            }
            /*商户交易限额*/
            int limitFlag = mchntService.checkMchntTransLimit(instId, mchntOrderInfo.getRealSettleAmt(), LimitAcctTypeEnum.ALL, mchntOrderInfo.getMchntNo(), null, txnNum);
            if (limitFlag == 2 || limitFlag == 3) {
                rglog.error("商户金额超限");
                throw new BizException(RespCodeEnum.MCHNT_AMT_OVER.getRespCode(), RespCodeEnum.MCHNT_AMT_OVER.getRespDesc());
            } else if (limitFlag == 1) {
                /*不支持信用卡*/
                xmlTreeUtil.setXmlTreeStringValue("disablePayChannels", "pcredit");
            }

            //step5.计算商户手续费 批量提供的jar包计算方法
            CalcMchntFeeBean clearSrcObj = new CalcMchntFeeBean();
            clearSrcObj.setMchntNo(mchntOrderInfo.getMchntNo());
            clearSrcObj.setTransAmt(mchntOrderInfo.getOrderAmt());
            clearSrcObj.setInstId(instId);
            clearSrcObj.setSubProdType(prodBizType);
            clearSrcObj.setStageCode("");
            clearSrcObj.setStageNum("");
            clearSrcObj.setIgasBizProdType(txnInfo.getSrcTxnAuthCfg().getBizProdCode());
            CalcMchntFee clearMethod = new CalcMchntFee();
            CalcMchntFeeResultBean calcMchntFeeResultBean = clearMethod.calcPayFee(clearSrcObj);
            if (null == calcMchntFeeResultBean) {
                rglog.error("计算商户手续费失败");
                throw new BizException(RespCodeEnum.CALC_MCHNT_FEE_ERROR.getRespCode(), RespCodeEnum.CALC_MCHNT_FEE_ERROR.getRespDesc());
            }
            //特殊费率商户设置费率标示
            if (CalcFlagEnum.CALCFLAG_SPEFEENOTCALC.getCalcFlag().equals(calcMchntFeeResultBean.getCalcFlag())) {
                mchntOrderInfo.setFeeInputMode("0");
            } else {
                mchntOrderInfo.setFeeInputMode(calcMchntFeeResultBean.getFeeInputMode());
            }
            mchntOrderInfo.setFeeRate(calcMchntFeeResultBean.getFeeRate());
            mchntOrderInfo.setFeeCappingAmt(calcMchntFeeResultBean.getFeeCappingAmt());
            mchntOrderInfo.setFeeFixedAmt(calcMchntFeeResultBean.getFeeFixedAmt());
            mchntOrderInfo.setMchntFee(calcMchntFeeResultBean.getMchntFee());
            mchntOrderInfo.setSysSeqNum(sysSeqNum);
            mchntOrderInfo.setTxnDate(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_DATE));
            mchntOrderInfo.setTxnTime(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_TIME));
            mchntOrderInfo.setMchntDiscountFee("0.00");
            mchntOrderInfo.setMchntSettleAmt(AmountUtil.subtraction(mchntOrderInfo.getRealSettleAmt(), calcMchntFeeResultBean.getMchntFee()));
            mchntOrderInfo.setTransStatus(TransStatusEnum.TRANS_INIT.getStatus());
            mchntOrderInfo.setBizStatus(BizStatusEnum.PROCESSING.getStatus());
            mchntOrderInfo.setRealSettleFlag(AppParamConstant.NO);
            mchntOrderInfo.setTxnNum(txnNum);
            mchntOrderInfo.setBizType(bizType);
            mchntOrderInfo.setBizProdCode(txnInfo.getSrcTxnAuthCfg().getBizProdCode());
            mchntOrderInfo.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
        }

        if (!updateOrderInfo(userOrderInfo, mchntOrderInfos, null)) {
            rglog.error("更新订单信息失败");
            throw new BizException(RespCodeEnum.UPDATE_ORDER_STATUS_ERROR.getRespCode(), RespCodeEnum.UPDATE_ORDER_STATUS_ERROR.getRespDesc());
        }

        //交易限额信息存入缓存
        xmlTreeUtil.setXmlTreeObjectValue(OpmsTreeNodeConstant.TRANS_LIMIT_INFO_LIST, transLimitBeanList);

        //step6.核对积分兑换金额 交易金额与积分转换后的金额是否相同
        if (OpmsParamConstant.STR_ONE.equals(acctType)) {
            IntegralUtil.integralExchangeCheck(instId, transAmt, transCode, OpmsParamConstant.SCORE_RATE_DR);
        } else if (OpmsParamConstant.STR_TWO.equals(acctType) || OpmsParamConstant.STR_THREE.equals(acctType)) {
            IntegralUtil.integralExchangeCheck(instId, transAmt, transCode, OpmsParamConstant.SCORE_RATE_CR);
        } else {
            rglog.info("卡类型不支持,cardType:<{}>", scorePayRequest.getAcctNo());
            throw new BizException(RespCodeEnum.PARAM_ERROR.getRespCode(), RespCodeEnum.PARAM_ERROR.getRespDesc());
        }

        //step7.营销手续费优惠计算、解析营销调用结果
        cc.rengu.igas.momp.facade.result.Result<ComboMchntFeeDiscountResponse> mompDubboResult
                = comboMchntFeeDiscount(scorePayRequest, mchntOrderInfos, mompChannelCallCfgList.get(0));
        analysisMompDubboRes(mompDubboResult, userOrderInfo, mchntOrderInfos, mompChannelCallCfgList.get(1), null);

        //step8.积分登记支付订单表
        PayOrderInfo payOrderInfo = registerScorePayOrderInfo(userOrderInfo, scorePayRequest, esbChannelCallCfgList.get(0), acctType);

        //TODO ste9.调用积分支付接口
        // （1） 通讯级失败（success=false），更新支付订单表/用户订单表/商户订单表，营销手续费退回mompReturnFee，抛出业务异常
        // （2） 交易超时，积分冲正，调用手续费回退 mompReturnFee，抛出业务异常
        // （3） 交易失败，更新 支付订单/用户订单/商户订单，调用手续费回退mompReturnFee，抛出具体异常
        // （4） 交易成功，更新订单，程序继续执行

        //商户限额更新

        //step11.异步给商户记虚拟账
        virtAcctMultiMchntAdjust(scorePayRequest, userOrderInfo, mchntOrderInfos);

        //输出结果
        ScorePayResponse scorePayResponse = new ScorePayResponse();
        scorePayResponse.setOrderAmt(transAmt);
        scorePayResponse.setActualAmt(AmountUtil.subtraction(transAmt, scorePayRequest.getCouponInfo().getDiscountAmt()));
        scorePayResponse.setDiscountAmt(scorePayRequest.getCouponInfo().getDiscountAmt());
        BizResponse<ScorePayResponse> bizResponse = new BizResponse<>();
        bizResponse.setRspSysId(AppParamConstant.SYS_ID);
        bizResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
        bizResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespDesc());
        bizResponse.setResult(scorePayResponse);
        return bizResponse;
    }

    @Override
    protected void bizAfterCust(Object response) throws Exception {
        BizResponse bizResponse = (BizResponse) response;
        ScorePayResponse scorePayResponse = (ScorePayResponse) bizResponse.getResult();
        ConvertUtil.convertInput(scorePayResponse);
    }

    /**
     * 积分支付登记支付订单表
     */
    private PayOrderInfo registerScorePayOrderInfo(UserOrderInfo userOrderInfo, ScorePayRequest request, ChannelCallCfg channelCallCfg, String acctType) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        PayOrderInfo payOrderInfo = new PayOrderInfo();
        BeanUtil.beanCopy(userOrderInfo, payOrderInfo);
        payOrderInfo.setSysSeqNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM));
        payOrderInfo.setTxnDate(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_DATE));
        payOrderInfo.setTxnTime(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_TIME));
        payOrderInfo.setTxnNum(channelCallCfg.getTxnNum());
        payOrderInfo.setBizType(channelCallCfg.getBizType());
        payOrderInfo.setOrderCcy(request.getOrderCcy());
        payOrderInfo.setOrderAmt(userOrderInfo.getRealPayAmt());
        payOrderInfo.setPayerAcctType(acctType);
        payOrderInfo.setPayType(PayTypeEnum.OTHER.getPayType());
        payOrderInfo.setPayeeAcctInfo("");
        payOrderInfo.setPayeeAcctType("");
        payOrderInfo.setMsgDstId(channelCallCfg.getCallChannelId());
        payOrderInfo.setDstTxnNum(channelCallCfg.getCallChannelTxn());
        payOrderInfo.setDstCallIndex(channelCallCfg.getCallIndex());
        String seqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM) + channelCallCfg.getCallIndex();
        payOrderInfo.setChannelReconKey(seqNum.substring(14));
        payOrderInfo.setChannelSeqNum(seqNum.substring(14));
        payOrderInfo.setBizStatus(BizStatusEnum.PROCESSING.getStatus());
        payOrderInfo.setTransStatus(TransStatusEnum.TRANS_INIT.getStatus());
        PayOrderInfoMapper payOrderInfoMapper = new PayOrderInfoMapperImpl();
        int recode = payOrderInfoMapper.insertPayOrderInfo(payOrderInfo);
        if (recode != 0) {
            throw new BizException(RespCodeEnum.INSERT_PAY_ORDER_ERROR.getRespCode(), RespCodeEnum.INSERT_PAY_ORDER_ERROR.getRespDesc());
        }
        return payOrderInfo;
    }

    /**
     * 获取账户类型和批量手续费计算产品类型
     *
     * @param sysParamService 系统参数组件
     * @param instId          法人机构号
     * @param acctNo          账号
     * @throws Exception 异常
     */
    private void getAcctType(SysParamService sysParamService, String instId, String acctNo) throws Exception {
        //获取法人行银联机构号
        SysParam sysParamInfo = sysParamService.getSysParamInfo(instId, OpmsParamConstant.OPMS_SYS_PARAM, OpmsParamConstant.CORPORATION_ORG_CODE);
        if (null == sysParamInfo || AppParamConstant.NO.equals(sysParamInfo.getParamStatus())) {
            rglog.error("法人机构号未在系统参数表配置银联机构号，instId:<{}>", instId);
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        String[] split = sysParamInfo.getParamValue().split(",");
        List<String> corporationorgList = Arrays.asList(split);
        //获取卡BIN
        BinInfoService binInfoService = new BinInfoServiceImpl();
        BinInfo binInfo = binInfoService.getCardBinInfo(acctNo);
        if (binInfo == null) {
            rglog.error("根据卡号<{}>查询卡bin信息失败", acctNo);
            throw new BizException(RespCodeEnum.GET_CARD_BIN_INFO_ERROR.getRespCode(), RespCodeEnum.GET_CARD_BIN_INFO_ERROR.getRespDesc());
        }
        //获取账户类型以及批量产品类型
        if (CardTypeEnum.DEBIT.getAcctType().equals(binInfo.getCardType())) {
            if (corporationorgList.contains(binInfo.getIssuerCode().substring(0, 4))) {
                acctType = AcctTypeEnum.BANK_DEBIT.getAcctType();
                prodBizType = FeeTypeEnum.BANK_DR_CARD.getFeeType();
            } else {
                acctType = AcctTypeEnum.OTHER_DEBIT.getAcctType();
                prodBizType = FeeTypeEnum.OTHER_BANK_DR_CARD.getFeeType();
            }
        } else if (CardTypeEnum.CREDIT.getAcctType().equals(binInfo.getCardType()) ||
                CardTypeEnum.QCREDIT.getAcctType().equals(binInfo.getCardType())) {
            if (corporationorgList.contains(binInfo.getIssuerCode().substring(0, 4))) {
                acctType = AcctTypeEnum.BANK_CREDIT.getAcctType();
                prodBizType = FeeTypeEnum.BANK_CR_CARD.getFeeType();
            } else {
                acctType = AcctTypeEnum.OTHER_DEBIT.getAcctType();
                prodBizType = FeeTypeEnum.OTHER_BANK_DR_CARD.getFeeType();
            }
        } else {
            rglog.error("不支持的卡类型<{}>", binInfo.getCardType());
            throw new BizException(RespCodeEnum.NOT_SURP_ACCTTYPE_ERROR.getRespCode(), RespCodeEnum.NOT_SURP_ACCTTYPE_ERROR.getRespDesc());
        }
    }

    /**
     * 密码校验
     *
     * @param scorePayRequest 请求体
     * @throws Exception 异常
     */
    private void checkPassword(ScorePayRequest scorePayRequest) throws Exception {

        String instId = scorePayRequest.getHeader().getInstId();
        String platMchntNo = scorePayRequest.getPlatMchntNo();
        String userId = scorePayRequest.getUserId();

        //获取平台商户信息
        String userInst = instId;
        MchntService mchntService = new MchntServiceImpl();
        PlatMchntInfo platMchntInfo = mchntService.getPlatMchntInfo(instId, platMchntNo);
        if (null != platMchntInfo) {
            if (SignTypeEnum.PLAT_CLASS_SIGN.getSignTypeCode().equals(platMchntInfo.getSignType())) {
                userInst = platMchntInfo.getPlatMchntNo();
            }
        }
        UserService userService = new UserServiceImpl();
        UserInfo userInfo = userService.getUserInfo(instId, userId, scorePayRequest.getHeader().getSession());
        if (userInfo == null || !"00".equals(userInfo.getUserStatus())) {
            rglog.error("用户不存在或用户已被注销");
            throw new BizException(RespCodeEnum.PARAM_ERROR.getRespCode(), RespCodeEnum.PARAM_ERROR.getRespDesc());
        } else if (userInfo.getTransPasswordErrorTimes() >= OpmsParamConstant.PASSWA_ERROR_TIME_LIMIT) {
            rglog.info("用户密码输入错误次数达到5次，无法发起支付");
            throw new BizException(RespCodeEnum.OVER_PASSWORD_ERROR_TIMES.getRespCode(), RespCodeEnum.OVER_PASSWORD_ERROR_TIMES.getRespDesc());
        }

        //校验是否设置密码
        if (userInfo.getTransPassword() == null || userInfo.getTransPassword().isEmpty()) {
            rglog.error("用户未设置密码,请先设置密码后支付");
            throw new BizException(RespCodeEnum.USER_NOT_SET_PASSWD_ERROR.getRespCode(), RespCodeEnum.USER_NOT_SET_PASSWD_ERROR.getRespDesc());
        }

        //校验密码 pan使用用户号
        HsmService hsmService = new HsmServiceImpl();
        String pin = hsmService.hsmTransPinRsaToDes(instId, "", "", userId, PinTypeEnum.DIGITAL, scorePayRequest.getPassword());
        boolean checkRes = hsmService.hsmVerifyPin(instId, "", "", userId, PinTypeEnum.DIGITAL, pin, userInfo.getTransPassword());

        //更新用户连续错误次数
        if (!checkRes) {
            userInfo.setTransPasswordErrorTimes(userInfo.getTransPasswordErrorTimes() + 1);
        } else {
            userInfo.setTransPasswordErrorTimes(0);
        }
        if (!userService.updateUserInfo(userInfo)) {
            rglog.info("更新用户信息表连续密码错误次数失败,passwordErrorTimes:<{}>", userInfo.getTransPasswordErrorTimes());
            throw new BizException(RespCodeEnum.USER_PASSWORD_ERROR.getRespCode(), RespCodeEnum.USER_PASSWORD_ERROR.getRespDesc());
        }
        //密码校验失败，交易终止
        if (!checkRes) {
            throw new BizException(RespCodeEnum.USER_PASSWORD_ERROR.getRespCode(), RespCodeEnum.USER_PASSWORD_ERROR.getRespDesc());
        }
    }


    /**
     * 手续费优惠计算接口
     *
     * @param request         请求体
     * @param mchntOrderInfos 商户订单集合
     * @param channelCallCfg  通道服务调用配置
     * @return DUBBO响应
     * @throws Exception 异常
     */
    private cc.rengu.igas.momp.facade.result.Result<ComboMchntFeeDiscountResponse> comboMchntFeeDiscount(ScorePayRequest request, List<MchntOrderInfo> mchntOrderInfos, ChannelCallCfg channelCallCfg) throws Exception {

        String paramKey = txnInfo.getSrcTxnAuthCfg().getTxnNum() + txnInfo.getSrcTxnAuthCfg().getBizType();
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParamInfo = sysParamService.getSysParamInfo(request.getHeader().getInstId(), OpmsParamConstant.MOMP_PROD_CODE, paramKey);
        if (null == sysParamInfo) {
            rglog.error("营销产品编码未配置,instId:<{}>", request.getHeader().getInstId());
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }

        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
        Header header = new Header();
        header.setSrcSysId(AppParamConstant.SYS_ID);
        header.setChanlId(OpmsParamConstant.OPM_SYS_ID);
        header.setBizType(AppParamConstant.DEFAULT_BIZ_TYPE);
        header.setTxnNum(channelCallCfg.getCallChannelTxn());
        header.setVersion("1.0.0");
        header.setTraceNo(sysSeqNum + channelCallCfg.getCallIndex());
        header.setTransTime(DateUtil.getCurrentTime());
        header.setTransDate(DateUtil.getCurrentDate());

        ComboMchntFeeDiscountRequest comboMchntFeeDiscountRequest = new ComboMchntFeeDiscountRequest();
        comboMchntFeeDiscountRequest.setBizProdCode(sysParamInfo.getParamValue());
        comboMchntFeeDiscountRequest.setMchntOrderId(request.getUserOrderId());
        comboMchntFeeDiscountRequest.setMchntNo(request.getPlatMchntNo());
        comboMchntFeeDiscountRequest.setTransAmt(request.getTransScoreCash());
        comboMchntFeeDiscountRequest.setUserId(request.getUserId());
        List<PaymentOrderMchntFeeDiscountInfoBean> paymentOrderDetailBeans = new ArrayList<>();
        convertOrderDetail(paymentOrderDetailBeans, mchntOrderInfos);
        comboMchntFeeDiscountRequest.setFeeOrderDetailList(paymentOrderDetailBeans);
        xmlTreeUtil.setXmlTreeObjectValue("comboMchntFeeDiscountRequest", comboMchntFeeDiscountRequest);
        DubboService dubboService = new DubboServiceImpl();
        return (cc.rengu.igas.momp.facade.result.Result<ComboMchntFeeDiscountResponse>) dubboService.callDubboService(OpmsParamConstant.DUBBO_MOMP_PAYMENT_SERVICE, "comboMchntFeeDiscount", comboMchntFeeDiscountRequest);
    }

    /**
     * 订单转换
     *
     * @param orderDetailList    请求营销订单明细
     * @param mchntOrderInfoList 商户订单集合
     */
    private void convertOrderDetail(List<PaymentOrderMchntFeeDiscountInfoBean> orderDetailList, List<MchntOrderInfo> mchntOrderInfoList) {
        for (MchntOrderInfo mchntOrderInfo : mchntOrderInfoList) {
            PaymentOrderMchntFeeDiscountInfoBean paymentOrderDetailBean = new PaymentOrderMchntFeeDiscountInfoBean();
            paymentOrderDetailBean.setMchntNo(mchntOrderInfo.getMchntNo());
            //手续费优惠试算 订单金额记商户清算金额
            paymentOrderDetailBean.setOrderAmt(mchntOrderInfo.getMchntSettleAmt());
            paymentOrderDetailBean.setFeeInputMode(mchntOrderInfo.getFeeInputMode());
            paymentOrderDetailBean.setMchntFeeReceivable(mchntOrderInfo.getMchntFee());
            if ("2".equals(mchntOrderInfo.getFeeInputMode())) {
                paymentOrderDetailBean.setMchntFeeReceivableRate("0");
            } else {
                paymentOrderDetailBean.setMchntFeeReceivableRate(mchntOrderInfo.getFeeRate());
            }
            orderDetailList.add(paymentOrderDetailBean);
        }
    }

    /**
     * 解析营销调用响应信息
     *
     * @param mompDubboResult   momp调用响应信息
     * @param userOrderInfo     用户订单信息
     * @param mchntOrderInfos   商户订单信息
     * @param mompReturnFeeMomp 营销交易码映射枚举类
     * @throws Exception 异常
     */
    private void analysisMompDubboRes(cc.rengu.igas.momp.facade.result.Result<ComboMchntFeeDiscountResponse> mompDubboResult, UserOrderInfo userOrderInfo,
                                      List<MchntOrderInfo> mchntOrderInfos, ChannelCallCfg mompReturnFeeMomp, PayOrderInfo mompPayOrderInfo) throws Exception {

        OrderServiceImpl orderService = new OrderServiceImpl();
        if (!mompDubboResult.isSuccess()) {
            rglog.error("营销手续费计算失败,整个交易失败");
            updateUserAndMchntOrderInf(userOrderInfo, mchntOrderInfos, mompPayOrderInfo);
            throw new BizException(RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespCode(), RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
        }

        ComboMchntFeeDiscountResponse mompResponse = mompDubboResult.getResult();
        if (OpmsParamConstant.DUBBO_OVERTIME.equals(mompResponse.getRespCode())) {

            rglog.error("调用营销手续费计算超时,交易失败，对营销发起手续费优惠回退");
            mompReturnFee(mchntOrderInfos, mompReturnFeeMomp);
            updateUserAndMchntOrderInf(userOrderInfo, mchntOrderInfos, mompPayOrderInfo);
            throw new BizException(RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespCode(), RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
        } else if (!OpmsParamConstant.DUBBO_SUCCESS.equals(mompResponse.getRespCode())) {

            rglog.error("营销手续费计算失败,整个交易失败");
            updateUserAndMchntOrderInf(userOrderInfo, mchntOrderInfos, mompPayOrderInfo);
            throw new BizException(RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespCode(), RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
        } else {

            rglog.info("营销手续费计算调用成功，开始解析响应内容");
            List<PaymentOrderMchntFeeDiscountInfoBean> feeOrderDetailList = mompResponse.getFeeOrderDetailList();
            if (feeOrderDetailList == null || feeOrderDetailList.isEmpty()) {
                rglog.error("营销返回数据有误,手续费优惠明细内容未给出");
                updateUserAndMchntOrderInf(userOrderInfo, mchntOrderInfos, mompPayOrderInfo);
                throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
            }

            //更新用户订单表和商户订单表数据
            for (MchntOrderInfo mchntOrderInfo : mchntOrderInfos) {
                Optional<PaymentOrderMchntFeeDiscountInfoBean> paymentOrderMchntFeeDiscountInfoBean = feeOrderDetailList.stream().filter(item -> item.getMchntNo().equals(mchntOrderInfo.getMchntNo())).findFirst();
                if (!paymentOrderMchntFeeDiscountInfoBean.isPresent()) {
                    rglog.error("营销返回的订单明细不存在");
                    throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
                }
                String mchntFeeDiscount = "0.00";
                String mchntFeeActual = "0.00";
                if (paymentOrderMchntFeeDiscountInfoBean.get().getMchntFeeDiscount() != null) {
                    mchntFeeDiscount = paymentOrderMchntFeeDiscountInfoBean.get().getMchntFeeDiscount();
                }
                if (paymentOrderMchntFeeDiscountInfoBean.get().getMchntFeeActual() != null) {
                    mchntFeeActual = paymentOrderMchntFeeDiscountInfoBean.get().getMchntFeeActual();
                }
                //订单优惠金额
                mchntOrderInfo.setOrderDiscountAmt("");
                //商户手续费优惠金额
                mchntOrderInfo.setMchntDiscountFee(mchntFeeDiscount);
                //实收商户手续费
                mchntOrderInfo.setRealMchntFee(mchntFeeActual);
                //商户结算金额
                mchntOrderInfo.setMchntSettleAmt(AmountUtil.subtraction(mchntOrderInfo.getRealSettleAmt(), mchntFeeActual));
                mchntOrderInfo.setRealSettleFlag(OpmsParamConstant.OPMS_PARAM_NO);
                mchntOrderInfo.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
            }

            mompPayOrderInfo.setSettleDate(DateUtil.getCurrentDate());
            mompPayOrderInfo.setRespDesc(mompResponse.getRespDesc());
            mompPayOrderInfo.setRespCode(mompResponse.getRespCode());
            mompPayOrderInfo.setBizStatus(BizStatusEnum.FINISH.getStatus());
            mompPayOrderInfo.setTransStatus(TransStatusEnum.TRANS_SUCC.getStatus());
            mompPayOrderInfo.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
            //更新商户订单信息
            if (!orderService.updateOrderInfo(null, mchntOrderInfos, mompPayOrderInfo)) {
                rglog.error("更新用户订单/支付订单表失败");
                throw new BizException(RespCodeEnum.UPDATE_ORDER_ERROR.getRespCode(), RespCodeEnum.UPDATE_ORDER_ERROR.getRespDesc());
            }
        }
    }

    /**
     * 手续费退回接口调用
     *
     * @param mchntOrderInfos 商户订单集合
     * @throws Exception 异常
     */
    private void mompReturnFee(List<MchntOrderInfo> mchntOrderInfos, ChannelCallCfg channelCallCfg) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
        ComboMchntFeeDiscountRequest comboMchntFeeDiscountRequest = (ComboMchntFeeDiscountRequest) xmlTreeUtil.getXmlTreeObjectValue("comboMchntFeeDiscountRequest", ComboMchntFeeDiscountRequest.class);

        Header header = new Header();
        header.setSrcSysId(AppParamConstant.SYS_ID);
        header.setChanlId(OpmsParamConstant.OPM_SYS_ID);
        header.setBizType(AppParamConstant.DEFAULT_BIZ_TYPE);
        header.setTxnNum(channelCallCfg.getCallChannelTxn());
        header.setVersion("1.0.0");
        header.setTraceNo(sysSeqNum + channelCallCfg.getCallIndex());
        header.setTransTime(DateUtil.getCurrentTime());
        header.setTransDate(DateUtil.getCurrentDate());

        ComboMchntFeeDiscountReversalRequest comboMchntFeeDiscountRollbackRequest = new ComboMchntFeeDiscountReversalRequest();
        comboMchntFeeDiscountRollbackRequest.setOrigTraceNo(comboMchntFeeDiscountRequest.getHeader().getTraceNo());
        DubboService dubboService = new DubboServiceImpl();
        Object object = dubboService.callDubboService(OpmsParamConstant.DUBBO_MOMP_PAYMENT_SERVICE, "comboMchntFeeDiscountReversal", comboMchntFeeDiscountRollbackRequest);
        cc.rengu.igas.momp.facade.result.Result<ComboMchntFeeDiscountReversalResponse> result = (cc.rengu.igas.momp.facade.result.Result<ComboMchntFeeDiscountReversalResponse>) object;
        if (null == result || !result.isSuccess() || OpmsParamConstant.DUBBO_SUCCESS.equals(result.getResult().getRespCode())) {
            rglog.error("手续费回退接口调用失败");
        }
    }

    /**
     * 更新用户订单表/商户订单表 交易状态为拒绝，业务状态为失败
     *
     * @param userOrderInfo      用户订单
     * @param mchntOrderInfoList 商户订单
     * @param mompPayOrderInfo   支付订单
     * @throws Exception 异常
     */
    private void updateUserAndMchntOrderInf(UserOrderInfo userOrderInfo, List<MchntOrderInfo> mchntOrderInfoList, PayOrderInfo mompPayOrderInfo) throws Exception {
        String currentDateTime = DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS");
        OrderService orderService = new OrderServiceImpl();
        //更新用户订单表/商户订单表状态为失败
        userOrderInfo.setTransStatus(TransStatusEnum.SYSTEM_REJECT.getStatus());
        userOrderInfo.setBizStatus(BizStatusEnum.FAILED.getStatus());
        userOrderInfo.setUpdateTime(currentDateTime);
        for (MchntOrderInfo mchntOrderInfo : mchntOrderInfoList) {
            mchntOrderInfo.setBizStatus(BizStatusEnum.FAILED.getStatus());
            mchntOrderInfo.setTransStatus(TransStatusEnum.SYSTEM_REJECT.getStatus());
            mchntOrderInfo.setUpdateTime(currentDateTime);
        }
        mompPayOrderInfo.setTransStatus(TransStatusEnum.SYSTEM_REJECT.getStatus());
        mompPayOrderInfo.setBizStatus(BizStatusEnum.FAILED.getStatus());
        mompPayOrderInfo.setUpdateTime(currentDateTime);
        if (!orderService.updateOrderInfo(userOrderInfo, mchntOrderInfoList, mompPayOrderInfo)) {
            rglog.error("更新用户订单/商户订单信息失败");
        } else {
            rglog.debug("更新用户订单/商户订单信息成功");
        }
    }

    /**
     * 商户虚拟账户记账
     *
     * @param scorePayRequest    请求实体
     * @param userOrderInfo      用户订单
     * @param mchntOrderInfoList 商户订单列表
     */
    private void virtAcctMultiMchntAdjust(ScorePayRequest scorePayRequest, UserOrderInfo userOrderInfo, List<MchntOrderInfo> mchntOrderInfoList) throws Exception {

        //虚拟账户记账或预授权完成，购买卡券交易和担保支付交易不记虚拟账户
        if ("00".equals(mchntOrderInfoList.get(0).getBizSceneType())
                || "01".equals(mchntOrderInfoList.get(0).getBizSceneType())
                || "02".equals(mchntOrderInfoList.get(0).getBizSceneType())) {
            rglog.info("购买卡券交易或担保支付的交易,bizSceneType:<{}>，不记商户虚拟账,但推送消息", mchntOrderInfoList.get(0).getBizSceneType());
            if (0 != asynnotify.transactionNotice("MchntTransNoticeService", 1, 0, 0)) {
                rglog.error("提交交易结果异步通知给商户失败!");
            }
            return;
        }

        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        DubboService dubboService = new DubboServiceImpl();
        MchntService mchntService = new MchntServiceImpl();
        List<ChannelCallCfg> ChannelCallCfgList = txnInfo.getChannelCallCfgList().stream().filter(channelCallCfg -> ChannelEnum.ACCT.getChannelType().equals(channelCallCfg.getCallChannelId())).collect(Collectors.toList());
        ChannelCallCfg channelCallCfg = ChannelCallCfgList.get(0);
        try {
            String sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
            PayOrderInfo acctPayOrderInfo = registerD1PayOrderInfo(userOrderInfo, scorePayRequest, channelCallCfg);
            if (acctPayOrderInfo == null) {
                rglog.error("登记虚拟账户记账支付订单失败");
                return;
            }

            VirtAcctMultiMchntAdjustRequest request = new VirtAcctMultiMchntAdjustRequest();
            cc.rengu.igas.acct.facade.base.Header header = new cc.rengu.igas.acct.facade.base.Header();
            BeanUtil.beanCopy(scorePayRequest.getHeader(), header);
            header.setSrcSysId(AppParamConstant.SYS_ID);
            header.setChanlId(OpmsParamConstant.OPM_SYS_ID);
            header.setTxnNum(channelCallCfg.getCallChannelTxn());
            header.setTraceNo(sysSeqNum + channelCallCfg.getCallIndex());
            request.setHeader(header);
            request.setAcctType("00");
            request.setDebitCreditFlag("C");
            List<MchtAdjustDetail> mchtAdjustDetailList = new ArrayList<>();
            for (MchntOrderInfo mchntOrderInfo : mchntOrderInfoList) {
                MchtAdjustDetail mchtAdjustDetail = new MchtAdjustDetail();
                MchntInfo mchntInfo = mchntService.getMchntInfo(mchntOrderInfo.getInstId(), mchntOrderInfo.getMchntNo());
                //非独立维护资料的门店商户需要获取连锁商户号去虚拟账户记账
                if ("11".equals(mchntInfo.getMchntType()) && AppParamConstant.NO.equals(mchntInfo.getIndependentDataFlag())) {
                    mchtAdjustDetail.setUserId(mchntInfo.getChainMchntNo());
                } else {
                    mchtAdjustDetail.setUserId(mchntOrderInfo.getMchntNo());
                }
                //01-商户
                mchtAdjustDetail.setUserType("01");
                mchtAdjustDetail.setTransAmt(mchntOrderInfo.getOrderAmt());
                mchtAdjustDetailList.add(mchtAdjustDetail);
            }
            request.setMchtAdjustDetailList(mchtAdjustDetailList);

            xmlTreeUtil.setXmlTreeObjectValue(OpmsTreeNodeConstant.PAY_ORDER_INFO, acctPayOrderInfo);
            int iReturnCode = dubboService.asynCallDubboService(OpmsParamConstant.DUBBO_ACCT_VIRTACCT_SERVICE, OpmsParamConstant.DUBBO_ACCT_VIRTACCTMULTIMCHNTADJUST, request, OpmsServiceConstant.ASYN_VIRTACCT_MULTIMCHNT_ADJUST_SERVICE);
            if (iReturnCode != 0) {
                rglog.error("异步虚拟账户记账服务注册失败！");
            } else {
                rglog.debug("异步虚拟账户记账服务注册成功！");
            }
        } catch (Exception e) {
            rglog.error("登记虚拟账户记账支付订单失败");
        }
    }

    /**
     * 虚拟账户订单入库
     *
     * @param userOrderInfo  用户订单实体
     * @param request        请求实体
     * @param channelCallCfg 交易映射码
     * @return 支付订单
     */
    private PayOrderInfo registerD1PayOrderInfo(UserOrderInfo userOrderInfo, ScorePayRequest request, ChannelCallCfg channelCallCfg) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        OrderService orderService = new OrderServiceImpl();
        PayOrderInfo payOrderInfo = new PayOrderInfo();
        BeanUtil.beanCopy(userOrderInfo, payOrderInfo);
        payOrderInfo.setTxnNum(request.getHeader().getTxnNum());
        payOrderInfo.setSysSeqNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM));
        payOrderInfo.setMsgDstId(channelCallCfg.getCallChannelId());
        payOrderInfo.setDstCallIndex(channelCallCfg.getCallIndex());
        payOrderInfo.setDstTxnNum(channelCallCfg.getCallChannelTxn());
        payOrderInfo.setChannelSeqNum("");
        payOrderInfo.setChannelReconKey(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM) + channelCallCfg.getCallIndex());
        payOrderInfo.setPayType(PayTypeEnum.BANK_CARD.getPayType());
        payOrderInfo.setPayerAcctType(AcctTypeEnum.BANK_ACCT.getAcctType());
        payOrderInfo.setBizStatus(BizStatusEnum.PROCESSING.getStatus());
        payOrderInfo.setTransStatus(TransStatusEnum.TRANS_INIT.getStatus());
        payOrderInfo.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
        if (!orderService.registerOrderInfo(null, null, payOrderInfo)) {
            return null;
        }
        return payOrderInfo;
    }

    @Override
    public ChannelCallCfg channelCallSceneDiscern(Object request, List<ChannelCallCfg> channelCallCfgList) throws Exception {
        Optional<ChannelCallCfg> channelCallCfgOpt = channelCallCfgList.stream().filter(item ->
                item.getBizScene().equals(ShareParamConstant.CALL_CFG_DEFAULT_BIZ_SCENE)).findFirst();
        return channelCallCfgOpt.orElse(null);
    }

    @Override
    public CheckResultEnum channelCallPreCheck(Object request, ChannelCallCfg channelCallCfg, OrderInfo orderInfo) throws Exception {
        return CheckResultEnum.PASSED;
    }

    @Override
    public TransStatusEnum channelCallResultProc(Object request, ChannelCallCfg channelCallCfg, TransStatusEnum transStatusEnum, Object channelResponse, OrderInfo needUpdateOrderInfo) throws Exception {
        return transStatusEnum;
    }
}
